package es.srn.utils.temporal;	

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;

/**
 *
 * @author Sergio Rubio
 * @version 16.05.2013
 * 
 * - Modificaciones
 * casa 04.11.2014
 */
public class SqLite {
    
    //La conexión
    private Connection conn = null;    
    private String databasePath =  null;
    private String nombreBD = null;
    
    private static final String RELATIVE_PATH_BD = "jdbc:sqlite:resource/";
    
    //url1 = "jdbc:sqlite:resource/personajesCombate.sqlite";
    /**
     * Constructor.
     * @param nombreBD Nombre del archivo de base de datos. Ej: bd.sqlite
     */
    public SqLite(String nombreBD){    
        try {
            this.nombreBD = nombreBD;
            Class.forName("org.sqlite.JDBC"); 
            this.databasePath = RELATIVE_PATH_BD + nombreBD;
            
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(SqLite.class.getName()).log(Level.SEVERE, null, ex);
            JOptionPane.showMessageDialog(null, ex.getMessage());
        }
    }
    
    /**
     * Abre y cierra la conexión a la base de datos.
     * @return true si funciona correctamente
     * @exception Genérica.
     */
    public boolean test(){        
        try{
            conectar();
            closeConnection();
            //System.out.println("Exito en el text: " + nombreBD);
            return true;
        }catch(Exception ex){
            throw ex;
        }
    }
    
    private void conectar(){                  
        try {
            //System.out.println("conectamos");
            conn = DriverManager.getConnection(databasePath);  
            
            //lanzo el evento
            //System.out.println("Lanzando el puto evento de abierto.");
            this.onBDOpen(nombreBD, conn);
        }catch (SQLException e) {
            JOptionPane.showMessageDialog(null, e.getMessage());        
        }
    }
    
    /**
     * Cierra la conexión con la base de datos.
     */
    public void closeConnection(){
        //System.out.println("desconectamos");
        if(conn != null){
            
            //inicializamos el autocommit a true
            try {
                conn.setAutoCommit(true);
            } catch (SQLException ex) {
                Logger.getLogger(SqLite.class.getName()).log(Level.SEVERE, null, ex);
                JOptionPane.showMessageDialog(null, ex.getMessage());
            }
            
            if(this.isOpen()){
                try {                
                    conn.close();
                    conn = null;
                } catch (SQLException ex) {
                    Logger.getLogger(SqLite.class.getName()).log(Level.SEVERE, null, ex);
                    JOptionPane.showMessageDialog(null, ex.getMessage());
                }                
            }
            //lanzo el evento
            //System.out.println("Lanzando el puto evento de cerrado.");
            this.onBDClose(nombreBD, conn);
        }       
    }
    
    /**
     * Comprueba si la conexión con la base de datos está abierta.
     * @return True si se encuentra abierta
     */
    public boolean isOpen(){
        if(conn == null){
            return false;
        }        
        try {
            return (conn.isClosed() ? false : true);
        } catch (SQLException ex) {
            Logger.getLogger(SqLite.class.getName()).log(Level.SEVERE, null, ex);
            JOptionPane.showMessageDialog(null, ex.getMessage());
            return false;
        }
    }
    
    /**
     * Comprueba si se está realizando una transacción.
     * @return True en caso afirmativo
     */
    public boolean isTransacction(){
        //System.out.println("isTransacction Comprobamos si conn es null....");
        if (conn != null)
            try {
                //System.out.println(" .... pues no es null, pero transacción: " + conn.getAutoCommit());
                return conn.getAutoCommit() ? false : true;
            } catch (SQLException ex) {
                Logger.getLogger(SqLite.class.getName()).log(Level.SEVERE, null, ex);
                JOptionPane.showMessageDialog(null, ex.getMessage());
                return false;
            }
        else{
            //System.out.println(" .... Joder, es null");
            return false;
        }
    }
    
    /**
     * Inicia una transacción.
     */
    public void iniciarTransaccion(){
        if (conn == null)
            conectar();
        
        try {
            conn.setAutoCommit(false);
        } catch (SQLException ex) {
            Logger.getLogger(SqLite.class.getName()).log(Level.SEVERE, null, ex);
            JOptionPane.showMessageDialog(null, ex.getMessage());
        }
    }
    
    /**
     * Termina una transacción ok o ko según se le indique
     * @param ok True si queremos realizar commit, false para rollback
     */
    public void terminarTransaccion(Boolean ok){
        try {
            if (ok)
                conn.commit();
            else
                conn.rollback();
            
            conn.setAutoCommit(true);
            
        } catch (SQLException ex) {
            Logger.getLogger(SqLite.class.getName()).log(Level.SEVERE, null, ex);
        }finally{
            closeConnection();
        }
    }
    
    /**
     *
     * @param sentencia
     * @return
     * @throws Exception
     */
    public int executeUpdate(String sentencia) throws Exception{
        int ret = 0;
        PreparedStatement sent = null;
        //System.out.println("Entrando en el executeUpdate, con la siguiente sentencia: \n " + sentencia);
        try{
            if (conn == null)
                this.conectar();
            
            
            //System.out.println("Preparamos el prepareStatement");
            sent = conn.prepareStatement(sentencia);            
            //System.out.println("Ejecutamos el prepareStatement");
            ret = sent.executeUpdate();
            //System.out.println("Resultado: " + ret);
            return ret;
        }catch (SQLException ex) {               
            throw ex;
        } catch (Exception e) {                
            throw e;
        }finally{
            //System.out.println("Cerramos conexiónes, finally del insert");
            if (sent != null){
                try { sent.close(); } catch (Exception e) { throw e; }
            }            
            if (conn != null)
            {                
                if (!isTransacction())                
                {
                    //System.out.println("por aqui no, capullo, es cerrar el conn en el insert.");
                    try { conn.close(); } catch (Exception e) { throw e; }
                }
                
            }
        }        
    }
    
    /**
     * Ejecuta una sentencia con devolución de datos
     * @param sent
     * @param sentencia
     * @return
     * @throws Exception
     */
    public ResultSet executeSelect(Statement sent, String sentencia) throws Exception{
        
        ResultSet ret;
        
       // System.out.println("Entrando en el select, con la siguiente sentencia: \n " + sentencia);
        try{
            if (conn == null)
                this.conectar();
            
            //System.out.println("Preparamos el prepareStatement: " + sentencia);
            sent = conn.createStatement();            
            //System.out.println("Ejecutamos el prepareStatement");
            ret = sent.executeQuery(sentencia); 
            return ret;        
        } catch (Exception e) {                
            throw e;        
        }
        /*
        finally{
            System.out.println("Cerramos conexiónes, finally del select (sent)");
            //if (sent != null){
                //try { sent.close(); } catch (Exception e) { throw e; }
            //}    
            System.out.println("Cerramos conexiónes, finally del select (conn)");
            if (conn != null)
            {                
                if (!isTransacction() && closeAutomatico)                
                {
                    System.out.println("Por aqui no, capullo, es cerrar el conn en el select si está transanccion.");
                    try { closeConnection(); } catch (Exception e) { throw e; }
                }
                
            }
        }   */     
    }
    
    /**
     *
     * @return
     * @throws Exception
     */
    public Statement GenerarComando() throws Exception
    {   
        try
        {
            if (conn == null)
                this.conectar();

            return conn.createStatement();
        }            
        catch (Exception e)
        {
            throw e;
        }            
    }
    
    /**
     *
     * @return
     */
    public String displayDbProperties()
    {
        java.sql.DatabaseMetaData dm = null;
        java.sql.ResultSet rs = null;
        StringBuilder tarea = new StringBuilder();
        
        try
        {
            if(conn == null){
                this.conectar();
            }
            
            
            if(conn!=null)
            {
                dm = conn.getMetaData();
                tarea.append("Driver Information\n");
                tarea.append("\tDriver Name: ").append(dm.getDriverName()).append("\n");
                tarea.append("\tDriver Version: ").append(dm.getDriverVersion ()).append("\n");
                tarea.append("\nDatabase Information \n");
                tarea.append("\tDatabase Name: ").append(dm.getDatabaseProductName()).append("\n");
                tarea.append("\tDatabase Version: ").append(dm.getDatabaseProductVersion()).append("\n");
                tarea.append("Avalilable Catalogs \n");
                rs = dm.getCatalogs();
                while(rs.next())
                {
                     tarea.append("\tcatalog: ").append(rs.getString(1)).append("\n");
                } 
                rs.close();
                rs = null;
                closeConnection();                
                
               }else tarea.append("Error: No active Connection\n");
            
            
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }finally{
            if(conn == null){
                this.closeConnection();
            }
        }
        
        dm=null;
        
        return tarea.toString();
        
    }   
    ////////////////// Inicio eventos
    //paso 1 - crear el listener
    /**
     *
     */
    public static interface sqlListener extends EventListener{                          
        /**
         *
         * @param nombreBD
         * @param conn
         */
        void onBDOpen(String nombreBD, Connection conn );
        /**
         *
         * @param nombreBD
         * @param conn
         */
        void onBDClose(String nombreBD, Connection conn );
        
    }
    
    //paso 2- manejo del array de listeners
    private List<sqlListener> sqlIlisteners=new ArrayList<>();
    
    /**
     *
     * @param listener
     */
    public void addSqlListener(sqlListener listener){
        sqlIlisteners.add(listener);
    }
    
    /**
     *
     * @param listener
     */
    public void removeSqlListener(sqlListener listener){
        sqlIlisteners.remove(listener);
    }
    
    //paso 3 - funciones de los eventos
    /**
     *
     * @param nombreBD
     * @param conn
     */
    protected void onBDOpen(String nombreBD, Connection conn){
        for(sqlListener listener: sqlIlisteners)
            listener.onBDOpen(nombreBD, conn);            
    }
    
    /**
     *
     * @param nombreBD
     * @param conn
     */
    protected void onBDClose(String nombreBD, Connection conn){
        for(sqlListener listener: sqlIlisteners)
            listener.onBDClose(nombreBD, conn);
    }    
    ////////////////// Fin eventos
    
    
}
